using System;
using System.Collections.Generic;
using System.Text;
using Exceptions;
using RJB.S3.Entities;

namespace RJB.S3Library
{
    public class FileManager : Manager, IFileManager
    {

        public FileManager(string UserKey, string SecretKey) : base(UserKey, SecretKey)
        {
        }
        
        /// <summary>
        /// Saves a file to a folder
        /// </summary>
        /// <param name="FileToSave">The file object to save</param>
        public void SaveFile(File FileToSave)
        {
            try
            {
                AmazonS3 _s3 = new AmazonS3();

                DateTime _timestamp = CurrentTimeStamp();
                string _sig = GenerateSignature(AmazonMethods.PutObjectInline, _timestamp);

                _s3.PutObjectInline(FileToSave.FolderName, FileToSave.Name, FileToSave.MetaData.ToArray(), FileToSave.Contents, FileToSave.Size, null, StorageClass.STANDARD, true,
                                    this.UserKey, _timestamp, true, _sig, null);
            }
            catch
            {
                throw new FileSaveException("Error Saving File.");
            }
        }
        /// <summary>
        /// Gets a file from S3 excluding meta data
        /// </summary>
        /// <param name="FileName">The file name (object name)</param>
        /// <param name="FolderName">The folder name (bucket name)</param>
        /// <returns>A file object</returns>
        public File GetFile(string FileName, string FolderName)
        {
            return GetFile(FileName, FolderName, true, false);   
        }
        /// <summary>
        /// Gets the metadata for a file from S3
        /// </summary>
        /// <param name="FileName">The file name (object name)</param>
        /// <param name="FolderName">The folder name (bucket name)</param>
        /// <returns>A file object</returns>
        public File GetMetaData(string FileName, string FolderName)
        {
            return GetFile(FileName, FolderName, false, true);
        }
        /// <summary>
        /// Gets the file object and metadata from S3
        /// </summary>
        /// <param name="FileName">The file name (object name)</param>
        /// <param name="FolderName">The folder name (bucket name)</param>
        /// <returns>A file object</returns>
        public File GetFullFile(string FileName, string FolderName)
        {
            return GetFile(FileName, FolderName, true, true);
        }
        /// <summary>
        /// The private method that retrieves the correct data
        /// from S3
        /// </summary>
        /// <param name="FileName">The file name (object name)</param>
        /// <param name="FolderName">The folder name (bucket name)</param>
        /// <param name="GetData"></param>
        /// <param name="GetMetaData"></param>
        /// <returns>A file object</returns>
        private File GetFile(string FileName, string FolderName, bool GetData, bool GetMetaData)
        {
            try
            {
                AmazonS3 _s3 = new AmazonS3();
                DateTime _timestamp = CurrentTimeStamp();

                GetObjectResult _result;
                //If we're not getting the data, don't get it inline
                //S3 excepts this to be false for metadata only queries
                if ( !GetData)
                    _result = _s3.GetObject(FolderName, FileName, GetMetaData, GetData, false, this.UserKey, _timestamp, true, this.GenerateSignature(AmazonMethods.GetObject, _timestamp), null);
                else 
                    _result = _s3.GetObject(FolderName, FileName, GetMetaData, GetData, true, this.UserKey, _timestamp, true, this.GenerateSignature(AmazonMethods.GetObject, _timestamp), null);
                
                File _f = new File(FileName, FolderName, "");
                _f.MD5Hash = _result.ETag;
                _f.Contents = _result.Data;
                _f.LastModified = DateTime.Now;

                
                //Convert the metadata
                if (_result.Metadata.GetLength(0) > 0)
                {
                    foreach (MetadataEntry entry in _result.Metadata)
                    {
                        MetadataEntry _meta = new MetadataEntry();
                        _meta.Name = entry.Name;
                        _meta.Value = entry.Value;
                        _f.MetaData.Add(_meta);
                    }
                    
                }

                return _f;
            }
            catch
            {
                throw new FileLoadException("Error Loading File.");
            }
        }
        
        /// <summary>
        /// Deletes a file from S3
        /// </summary>
        /// <param name="FileName">The file name (object) that needs to be deleted</param>
        /// <param name="FolderName">The folder name (bucket) that holds the object</param>
        public void DeleteFile(string FileName, string FolderName)
        {
            AmazonS3 _s3 = new AmazonS3();
            DateTime _timestamp = CurrentTimeStamp();

            Status _status = _s3.DeleteObject(FolderName, FileName, this.UserKey, _timestamp, true, this.GenerateSignature(AmazonMethods.DeleteObject, _timestamp), null);
            if (_status.Code != 200)
                throw new FileDeleteException("The File has not been deleted.  It failed with the following error: " + _status.Description);
        }
        /// <summary>
        /// Retrieves a generic list of Files
        /// </summary>
        /// <param name="FolderName">The folder (bucket) that holds the files</param>
        /// <returns>A generic list of Files</returns>
        public List<File> ListFiles(string FolderName)
        {
            try
            {
                AmazonS3 _s3 = new AmazonS3();
                DateTime _timestamp = CurrentTimeStamp();

                ListBucketResult _result = _s3.ListBucket(FolderName, null, null, 0, false, null, this.UserKey, _timestamp, true, this.GenerateSignature(AmazonMethods.ListBucket, _timestamp), null);

                List<File> _files;
                if (_result.Contents != null)
                {
                     _files = new List<File>(_result.Contents.Length);

                    foreach (ListEntry entry in _result.Contents)
                    {
                        File _f = new File(entry.Key, FolderName, entry.Owner.DisplayName);
                        _f.MD5Hash = entry.ETag;
                        _f.Size = entry.Size;
                        _f.LastModified = entry.LastModified;

                        _files.Add(_f);
                    }
                    return _files;
                }
                else
                {
                    return new List<File>();
                }
                
            }
            catch
            {
                throw new FileListException("Error Listing Files.");
            }
            
        }
    }
}
